import json
import random
import time
from urllib.parse import urlencode, quote
from typing import Tuple, Dict, List, Optional
import requests
from bs4 import BeautifulSoup
from ddddocr import DdddOcr
import Config
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel

from logging_config import configure_logger
from proxy import get_proxy_from_cache
from pycrypt import PrpCrypt

app = FastAPI()
logger = configure_logger()  # 使用配置好的日志记录器


class ZxgkSpider:
    def __init__(self):
        self.session = requests.Session()
        self.ocr = DdddOcr()
        self.reset_result_data()
        self.cap_id = None
        self.code_text = None
        self.username = Config.USERNAME
        self.password = Config.PASSWORD
        self.proxy_ip = None
        self.proxies = {}
        self.ua = random.choice(Config.USER_AGENTS)
        if Config.USEPROXY:
            self.proxy_ip = get_proxy_from_cache(Config.USERNAME, Config.PASSWORD)
            if self.proxy_ip:
                self.proxies = {
                    "http": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": Config.USERNAME, "pwd": Config.PASSWORD,
                                                                    "proxy": self.proxy_ip},
                    "https": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": Config.USERNAME, "pwd": Config.PASSWORD,
                                                                     "proxy": self.proxy_ip}
                }

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.session.close()
        return True

    def reset_result_data(self):
        """重置结果数据"""
        self.result_data = {
            "isZhiXing": False,
            "isShixin": False,
            "isXgl": False
        }

    def get_headers(self, header_type):
        """获取合并后的headers"""
        headers = Config.HEADERS['base'].copy()
        headers.update(self.ua)
        headers.update(Config.HEADERS.get(header_type, {}))
        return headers

    def _generate_random_id(self, length: int = 32) -> str:
        """生成随机ID"""
        chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
        return ''.join(random.choice(chars) for _ in range(length))

    def _get_captcha_info(self) -> Tuple[str, str]:
        """获取验证码URL和ID"""
        cap_id = self._generate_random_id()
        img_url = f"{Config.BASE_URL}/captcha.do?captchaId={cap_id}&random={random.random()}"
        return img_url, cap_id

    def _download_captcha(self, img_url: str) -> bytes:
        """下载验证码图片"""
        try:
            response = self.session.get(img_url, headers=self.get_headers('captcha'), proxies=self.proxies)
            logger.info(f"获取验证码响应状态码: {response.status_code}")
            response.raise_for_status()
            time.sleep(Config.DEFAULT_WAIT_TIME)
            return response.content
        except requests.RequestException as e:
            raise Exception(f"下载验证码失败: {str(e)}")

    def _recognize_captcha(self, image_content: bytes) -> str:
        """识别验证码"""
        try:
            return self.ocr.classification(image_content)
        except Exception as e:
            raise Exception(f"验证码识别失败: {str(e)}")

    def _validate_captcha(self, cap_id: str, code_text: str) -> bool:
        """验证验证码是否正确"""
        try:
            url = f"{Config.BASE_URL}/checkyzm?captchaId={cap_id}&pCode={code_text}"
            response = self.session.get(url, headers=self.get_headers('check_code'), proxies=self.proxies)
            logger.info(f"验证码: {code_text}, 验证响应状态码: {response.status_code}")
            response.raise_for_status()
            time.sleep(Config.DEFAULT_WAIT_TIME)
            return response.text.strip() == "1"
        except requests.RequestException as e:
            logger.error(f"验证码验证失败: {str(e)}")
            return True

    def get_valid_captcha(self):
        """获取有效的验证码"""
        for attempt in range(Config.MAX_RETRIES):
            try:
                img_url, cap_id = self._get_captcha_info()
                img_content = self._download_captcha(img_url)
                code_text = self._recognize_captcha(img_content)

                if self._validate_captcha(cap_id, code_text):
                    self.cap_id = cap_id
                    self.code_text = code_text
                    return
            except Exception as e:
                logger.error(f"获取验证码尝试失败({attempt + 1}/{Config.MAX_RETRIES}): {str(e)}")
                if attempt == Config.MAX_RETRIES - 1:
                    raise
                time.sleep(1)
        raise Exception("无法获取有效验证码，请稍后再试")

    def _parse_detail_page(self, html_content: str) -> bool:
        """解析详情页HTML，设置状态标志"""
        bs = BeautifulSoup(html_content, "html.parser")
        sections = bs.find_all("div", class_="col-lg-12 col-md-12 col-sm-12 row-block")

        for section in sections:
            title = section.find("div", class_="col-lg-12 col-md-12 col-sm-12 bg-title")
            if not title:
                continue

            case_category = title.text.strip()
            if case_category == "限制消费人员":
                self.result_data.update({"isZhiXing": True, "isShixin": True, "isXgl": True})
                return False  # 最高级别，无需继续查询
            elif case_category == "失信被执行人":
                self.result_data.update({"isZhiXing": True, "isShixin": True, "isXgl": False})
            elif case_category == "被执行人":
                self.result_data.update({"isZhiXing": True, "isShixin": False, "isXgl": False})

        return not all(self.result_data.values())  # 如果所有状态都为True则返回False

    def search_records(self, name: str, id_card: str, page: int = 1) -> Dict:
        """搜索失信记录（带重试机制）"""
        url = f"{Config.BASE_URL}/searchZhcx.do"

        for attempt in range(Config.RECORD_MAX_RETRIES):
            payload = {
                "pName": name,
                "pCardNum": id_card,
                "selectCourtId": 0,
                "pCode": self.code_text,
                "captchaId": self.cap_id,
                "searchCourtName": "全国法院（包含地方各级法院）",
                "selectCourtArrange": 1,
                "currentPage": page
            }
            try:
                response = self.session.post(url, headers=self.get_headers('search'), data=payload, proxies=self.proxies)
                response.raise_for_status()
                time.sleep(Config.DEFAULT_WAIT_TIME)
                json_data = response.json()
                return json_data[0] if json_data else {}
            except Exception as e:
                logger.error(f"搜索记录失败({attempt + 1}/{Config.RECORD_MAX_RETRIES}): {str(e)}")
                if attempt == Config.RECORD_MAX_RETRIES - 1:
                    logger.error("搜索记录失败！")
                    return {}
                self.get_valid_captcha()
        return {}

    def get_record_detail(self, case_code: str,
                          name: str, id_card: str, de_party_card_num: str) -> bool:
        """获取记录详情（带重试机制）"""
        url = f"{Config.BASE_URL}/detailZhcx.do"

        for attempt in range(Config.MAX_RETRIES):
            payload = {
                "pnameNewDel": name,
                "caseCodeNewDel": case_code,
                "j_captchaNewDel": self.code_text,
                "cardNumNewDel": id_card,
                "dePartyCardNumNewDel": de_party_card_num,
                "captchaIdNewDel": self.cap_id
            }

            try:
                response = self.session.post(url, headers=self.get_headers('detail'), data=urlencode(payload), proxies=self.proxies)
                response.raise_for_status()
                return self._parse_detail_page(response.text)
            except Exception as e:
                logger.error(f"获取详情失败({attempt + 1}/{Config.MAX_RETRIES}): {str(e)}")
                if attempt == Config.MAX_RETRIES - 1:
                    return True  # 出错时继续查询
                self.get_valid_captcha()
        return True

    def check_all_records_status(self, name: str, id_card: str) -> Tuple[str, Dict]:
        """检查所有记录状态"""
        self.reset_result_data()
        start_time = time.time()

        try:
            self.get_valid_captcha()

            first_page = self.search_records(name, id_card)

            if not first_page or not first_page.get("result"):
                logger.info("未找到记录")
                return "success", self.result_data

            total_page = first_page.get("totalPage", 1)
            page_range = range(total_page, 0, -1) if Config.REVERSE else range(1, total_page + 1)

            for page in page_range:
                if page == 1:
                    page_data = first_page
                else:
                    page_data = self.search_records(name, id_card, page)
                if not page_data or not page_data.get("result"):
                    continue

                records = reversed(page_data["result"]) if Config.REVERSE else page_data["result"]
                for record in records:
                    if not self.get_record_detail(
                            record.get("caseCode"),
                            name, id_card, record.get("dePartyCardNum")
                    ):
                        logger.info("查询到限制消费人员，停止查询")
                        return "success", self.result_data

            logger.info("查询完成，未发现限制消费人员")
            return "success", self.result_data

        except Exception as e:
            logger.error(f"查询异常: {str(e)}")
            return f"error: {str(e)}", {}
        finally:
            logger.info(f"查询耗时: {time.time() - start_time:.2f}秒")


class QueryRequest(BaseModel):
    name: str
    id_card: str


@app.post("/api/check_records")
async def check_records(request: Request):
    req = await request.json()
    body = req.get("body", "")
    try:
        pc = PrpCrypt(Config.KEY)  # 初始化密钥
        data = json.loads(pc.decrypt(body.encode()))  # 解密
    except Exception as e:
        logger.error(f"解密失败: {str(e)}")
        return {"status": "fail", "msg": str(e)}

    logger.info(f"接收到查询请求: {data}")
    name = data.get("name")
    id_card = data.get("id_card")
    with ZxgkSpider() as spider:  # 使用 with 管理 ZxgkSpider
        status, result = spider.check_all_records_status(
            name=name,
            id_card=id_card
        )
    logger.info(f"查询结果:{name}, {status}, {result}")
    if status.startswith("error"):
        return {"status": "fail", "msg": status}

    return {
        "status": status,
        "isZhiXing": result.get("isZhiXing", False),
        "isShixin": result.get("isShixin", False),
        "isXgl": result.get("isXgl", False)
    }


if __name__ == "__main__":
    import uvicorn

    uvicorn.run("zxgk_api_proxy:app", host="0.0.0.0", port=Config.PORT, workers=2)